//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.DataLayer
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;

    internal class SecurityHelper
    {
        private static string alternateSecurityLocation = null;

        private static List<SecurityPkgInfo> SecurityCache = null;

        /// <summary>
        /// Gets or sets the alternate security location.
        /// </summary>
        /// <value>The alternate security location.</value>
        internal static string AlternateSecurityLocation
        {
            get { return alternateSecurityLocation; }
            set { alternateSecurityLocation = value; }
        }

        /// <summary>
        /// Reloads the securitys.
        /// </summary>
        internal static void ReloadSecuritys()
        {
            SecurityCache = null;
        }

        /// <summary>
        /// Gets the security filters.
        /// </summary>
        /// <param name="secureConn">The secure conn.</param>
        /// <param name="conn">The conn.</param>
        /// <returns></returns>
        internal static List<IDataLayerSecurityFilter> GetSecurityFilters(ConnectionSecure secureConn, Connection conn)
        {
            List<SecurityPkgInfo> filters = GetSecurityFilterInfo();
            List<IDataLayerSecurityFilter> results = new List<IDataLayerSecurityFilter>();
            foreach (SecurityPkgInfo filter in filters)
            {
                IDataLayerSecurityFilter newFilter = filter.Factory(secureConn, conn);
                results.Add(newFilter);
            }
            return results;
        }

        /// <summary>
        /// Gets the security filter info.
        /// </summary>
        /// <returns></returns>
        internal static List<SecurityPkgInfo> GetSecurityFilterInfo()
        {
            if (SecurityCache != null)
                return SecurityCache;

            ResolveEventHandler resolveHandler = null;
            try
            {
                List<SecurityPkgInfo> ret = new List<SecurityPkgInfo>();

                resolveHandler = new ResolveEventHandler(CurrentDomain_AssemblyResolve);
                AppDomain.CurrentDomain.AssemblyResolve += resolveHandler;

                // Determine where we run from
                Assembly me = Assembly.GetExecutingAssembly();
                string myPath = (true == string.IsNullOrEmpty(alternateSecurityLocation)) ?
                    Path.GetDirectoryName(me.Location) : alternateSecurityLocation;

                // Fetch all assemblies in the folder
                foreach (string asmCandidate in Directory.GetFiles(myPath, "*.DataProvidersSecurity*.dll"))
                {
                    Assembly loadedCandidate = Assembly.LoadFile(asmCandidate);

                    System.Type provInfo = loadedCandidate.GetType("Microsoft.Research.DataLayer.SecurityInfo");
                    PropertyInfo provInfoInstance = provInfo.GetProperty("Filters", BindingFlags.Public | BindingFlags.Static);
                    SecurityPkgInfo[] provListInCandidate = (SecurityPkgInfo[])provInfoInstance.GetValue(null, null);
                    foreach (SecurityPkgInfo prov in provListInCandidate)
                    {
                        ret.Add(prov);
                    }
                }

                SecurityCache = ret;

                return ret;
            }
            finally
            {
            }
        }

        /// <summary>
        /// Handles the AssemblyResolve event of the CurrentDomain control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="args">The <see cref="System.ResolveEventArgs"/> instance containing the event data.</param>
        /// <returns></returns>
        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string asmFilePath = args.Name;
            string asmFile = Path.GetFileName(asmFilePath);

            try
            {
                //In case of assembly info create the dll name and try to load it from the current dir
                //if not try it with the AlternateSecurityLocation which will be set from GetSecuritys()
                //If we receive an actual path split will return only 1 string
                string[] strAssembly = asmFilePath.Split(',');
                if (strAssembly.Length > 1 && !string.IsNullOrEmpty(strAssembly[0]))
                {
                    asmFile = string.Format("{0}.dll", strAssembly[0]);
                }

                Assembly[] asms;
                asms = AppDomain.CurrentDomain.GetAssemblies();
                foreach (Assembly asm in asms)
                {
                    string loadedAsm = Path.GetFileName(asm.Location);
                    if (string.Equals(loadedAsm, asmFile, StringComparison.InvariantCultureIgnoreCase))
                    {
                        return asm;
                    }
                }
                Assembly try1 = Assembly.LoadFile(asmFilePath);
                return try1;
            }
            catch
            {
                if (!string.IsNullOrEmpty(AlternateSecurityLocation))
                {
                    string asmCandidate = Path.Combine(AlternateSecurityLocation, asmFile);

                    if (File.Exists(asmCandidate))
                    {
                        return Assembly.LoadFile(asmCandidate);
                    }
                }
            }
            return null;
        }
    }
}
